﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using WordSeg;
using CRFSharp;
using BusinessNameParserFeatureGenerator;

namespace POIParser
{
    public class Token
    {
        public string strTerm;
        public string strTag;
    }

    public class POIParser
    {
        WordSeg.WordSeg wordseg;
        WordSeg.Tokens wbTokens;
        BusinessNameParserFeatureGenerator.BusinessNameParserFeatureGenerator featureGenerator;

        const string ORG_MOD_LOC_TAG = "ORG_MOD_LOC";
        const string LOC_TAG = "LOC";
        const string ORG_MOD_TAG = "ORG_MOD";
        const string BRD_ORG_TAG = "BRD_ORG";

        const string LOC_CRF = "LOC_CRF";
        const string LOC_DICT = "LOC_DICT";

        public bool Initialize(string strLexicalDictionaryFileName, string strCRFModelFileName)
        {
            bool bRet = false;
            featureGenerator = new BusinessNameParserFeatureGenerator.BusinessNameParserFeatureGenerator();
            bRet = featureGenerator.Initialize();
            if (bRet == false)
            {
                return false;
            }

            wordseg = new WordSeg.WordSeg();
            //Load lexical dictionary
            wordseg.LoadLexicalDict(strLexicalDictionaryFileName, false);
            wordseg.LoadModelFile(strCRFModelFileName, featureGenerator);

            //Initialize word breaker's token instance
            wbTokens = wordseg.CreateTokens(1024);

            return true;
        }

        public List<Token> Parse(string strPOI)
        {
            wordseg.Segment(strPOI, wbTokens, true);
            List<Token> tokenList = new List<Token>();
            if (wbTokens.tokenList.Count == 0)
            {
                return tokenList;
            }

            for (int i = 0; i < wbTokens.tokenList.Count; i++)
            {
                Token token = new Token();
                token.strTerm = wbTokens.tokenList[i].strTerm;
                List<string> tagList = new List<string>();
                foreach (string strTag in wbTokens.tokenList[i].strTagList)
                {
                    tagList.AddRange(strTag.Split('|'));
                }
                
                //If the token has any kind of location tag in lexical dictionary,
                //Add a new tag DICT_LOC
                foreach (string item in tagList)
                {
                    if (item.StartsWith("DICT_LOC") == true)
                    {
                        tagList.Add("DICT_LOC");
                        break;
                    }
                }

                //CRF model label this token as core name in POI
                if (tagList.Contains("BRD_ORG") == true)
                {
                    token.strTag = BRD_ORG_TAG;
                }
                else if ((tagList.Contains("LOC") == true || tagList.Contains("LOC_ROD") == true) && 
                    tagList.Contains("DICT_LOC") == true)
                {
                    //Location tag need to double confirm by both CRF model and lexical dictionary
                    token.strTag = LOC_TAG;
                }
                else if (tagList.Contains("ORG_MOD_LOC") == true && tagList.Contains("DICT_LOC") == true)
                {
                    //Location tag need to double confirm by both CRF model and lexical dictionary
                    token.strTag = ORG_MOD_LOC_TAG;
                }
                else if (tagList.Contains("LOC") == true || tagList.Contains("LOC_ROD") == true || tagList.Contains("ORG_MOD_LOC") == true)
                {
                    token.strTag = LOC_CRF;
                }
                else if (tagList.Contains("LOC_DICT") == true && wbTokens.tokenList[i].offset == 0)
                {
                    token.strTag = LOC_DICT;
                }
                else
                {
                    token.strTag = ORG_MOD_TAG;
                }

                tokenList.Add(token);
            }

            //Rewrite some location tags
            int iAllLocTagCnt = 0;
            int iLocTagCnt = 0;
            foreach (Token item in tokenList)
            {

                if (item.strTag == LOC_TAG || item.strTag == ORG_MOD_LOC_TAG)
                {
                    //The number of tags related with locations
                    iAllLocTagCnt++;
                }

                if (item.strTag == LOC_TAG)
                {
                    //The number of location tags
                    iLocTagCnt++;
                }
            }

            //If there is no really location name, change the first ORG_MOD_LOC (if it is existed) to LOC
            if (iAllLocTagCnt > 0 && iLocTagCnt == 0)
            {
                foreach (Token item in tokenList)
                {
                    if (item.strTag == ORG_MOD_LOC_TAG)
                    {
                        item.strTag = LOC_TAG;
                        iLocTagCnt++;
                        break;
                    }
                }
            }

            //If we already got location name, rewrite all location candidates which is only recongzied by CRF model as non-location type
            if (iLocTagCnt > 0)
            {
                foreach (Token item in tokenList)
                {
                    if (item.strTag == LOC_CRF)
                    {
                        item.strTag = ORG_MOD_TAG;
                    }
                }
            }
            else
            {
                //Only save the first LOC_CRF tag
                for (int i = 0; i < tokenList.Count; i++)
                {
                    if (tokenList[i].strTag == LOC_CRF)
                    {
                        for (int j = i + 1; j < tokenList.Count; j++)
                        {
                            if (tokenList[j].strTag == LOC_CRF)
                            {
                                tokenList[j].strTag = ORG_MOD_TAG;
                            }
                        }
                        break;
                    }
                }
            }


            //If there are more than one location tags, only use the last one whose previous token is not a location
            //For other location tags, if they are not the parent or child of the last one, change its tag to ORG_MOD_LOC
            if (iLocTagCnt > 1)
            {
                bool bFoundSelectedTag = false;
                string strSelectedLocation = "";
                int locIdx = -1;
                for (int i = tokenList.Count - 1; i >= 0; i--)
                {
                    if (tokenList[i].strTag == LOC_TAG && 
                        i > 0 &&
                        tokenList[i-1].strTag != LOC_TAG &&
                        tokenList[i-1].strTag != ORG_MOD_LOC_TAG)
                    {
                        bFoundSelectedTag = true;
                        strSelectedLocation = tokenList[i].strTerm;
                        locIdx = i;
                        break;
                    }
                }

                //Check whether the first token is a location canidates
                if (bFoundSelectedTag == false && tokenList[0].strTag == LOC_TAG)
                {
                    bFoundSelectedTag = true;
                    strSelectedLocation = tokenList[0].strTerm;
                }

                if (bFoundSelectedTag == true)
                {
                    //Rewrite other location candidates
                    for (int i = tokenList.Count - 1; i >= 0; i--)
                    {
                        if (locIdx == i)
                        {
                            continue;
                        }

                        //The rewritten location canidates should be neither parent nor child of selected location
                        if (tokenList[i].strTag == LOC_TAG &&
                            featureGenerator.IsParent(strSelectedLocation, tokenList[i].strTerm) == false &&
                            featureGenerator.IsParent(tokenList[i].strTerm, strSelectedLocation) == false
                            )
                        {
                            tokenList[i].strTag = ORG_MOD_LOC_TAG;
                        }
                    }
                }
            }


            //Finaly process
            bool bHasLoc = false;
            for (int i = 1; i < tokenList.Count; i++)
            {
                if (tokenList[i].strTag.Contains("LOC") == true)
                {
                    bHasLoc = true;
                    break;
                }
            }
            if (bHasLoc == true && tokenList[0].strTag == LOC_DICT)
            {
                tokenList[0].strTag = ORG_MOD_TAG;
            }

            return tokenList;
        }
    }
}
